#include "StdDevice_GPIO.h"
#include "StdDevice_common.h"

#define LOG __platform_printf



#if 0
static pika_dev* _get_dev(PikaObj* self) {
    pika_dev* dev = PIKA_HAL_OBJ2DEV(self);
    if (NULL != dev) {
        return dev;
    }
    dev = pika_hal_open(PIKA_HAL_GPIO, obj_getStr(self, "name"));
    if (NULL == dev) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] open GPIO '%s' failed.",
                          obj_getStr(self, "name"));
    }
    obj_setPtr(self, "pika_dev", dev);
    return dev;
}
#else
static pika_dev* _get_dev(PikaObj* self) {
    pika_dev* dev = PIKA_HAL_OBJ2DEV(self);
    if (NULL == dev) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] should first call open.");
        return NULL;
    }
    return dev;
}

#endif


void StdDevice_GPIO___init__(PikaObj *self, char* name){
    obj_setPtr(self, "pika_dev", NULL);
    obj_setStr(self, "name", name);
}




void StdDevice_GPIO_open(PikaObj *self, int mode, int pull, int speed){
    // pika_dev* dev = _get_dev(self);
    pika_dev* dev = PIKA_HAL_OBJ2DEV(self);
    //不允许重复调用pika_hal_open
    if (NULL != dev) {
        return;
    }
    dev = pika_hal_open(PIKA_HAL_GPIO, obj_getStr(self, "name"));
    if (NULL == dev) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] open GPIO '%s' failed.",obj_getStr(self, "name"));
        return;
    }
    obj_setPtr(self, "pika_dev", dev);
    // LOG("<%s> dev->is_enabled=%ld\n", __func__, dev->is_enabled);
    // pika_hal_ioctl(dev, PIKA_HAL_IOCTL_ENABLE);
    // LOG("<%s> dev->is_enabled=%ld\n", __func__, dev->is_enabled);
    pika_hal_GPIO_config cfg = {0};
    cfg.pull = pull;
    cfg.dir = mode;
    cfg.speed = speed;
    int err = -1;
    err = pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg);
    if (err != 0) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] open '%s' failed.", obj_getStr(self, "name"));
        return;
    }
    err = pika_hal_ioctl(dev, PIKA_HAL_IOCTL_ENABLE);
    if (err != 0) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] open '%s' failed.", obj_getStr(self, "name"));
        return;
    }
}

void StdDevice_GPIO_reload(PikaObj *self){
    pika_dev* dev = PIKA_HAL_OBJ2DEV(self);
    //不允许重复调用pika_hal_open
    if (NULL != dev) {
        return;
    }
    dev = pika_hal_open(PIKA_HAL_GPIO, obj_getStr(self, "name"));
    if (NULL == dev) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] reload GPIO '%s' failed.",obj_getStr(self, "name"));
        return;
    }
    obj_setPtr(self, "pika_dev", dev);
    int err = -1;
    err = pika_hal_ioctl(dev, PIKA_HAL_IOCTL_ENABLE);
    if (err != 0) {
        obj_setErrorCode(self, PIKA_RES_ERR_RUNTIME_ERROR);
        obj_setSysOut(self, "[ERROR] reload '%s' failed.", obj_getStr(self, "name"));
        return;
    }
}

void StdDevice_GPIO_iomux(PikaObj *self, int altfunc){
    pika_hal_GPIO_config cfg = {0};
    int *altfunc_ptr = (int *)pikaMalloc(sizeof(int));
    *altfunc_ptr = altfunc;
    cfg.user_data = altfunc_ptr;
    pika_dev* dev = _get_dev(self);
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg);
    pikaFree(altfunc_ptr, sizeof(int));
}


void StdDevice_GPIO_close(PikaObj *self){
    pika_dev* dev = _get_dev(self);
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_DISABLE);
    pika_hal_close(dev);
    obj_setPtr(self, "pika_dev", NULL);
}

void StdDevice_GPIO___del__(PikaObj *self){
    pika_dev* dev = PIKA_HAL_OBJ2DEV(self);
    if (NULL != dev && NULL != dev->ioctl_config) {
        pikaFree(dev->ioctl_config, sizeof(pika_hal_GPIO_config));
        dev->ioctl_config = NULL;
    }
    if (NULL != dev) {
        pikaFree(dev, sizeof(pika_dev));
    }
}


char* StdDevice_GPIO_get_name(PikaObj *self){
    return obj_getStr(self, "name");
}

void StdDevice_GPIO_write(PikaObj *self, int lv){
    pika_dev* dev = _get_dev(self);
    uint32_t val = lv == 0 ? 0 : 1;
    pika_hal_write(dev, &val, sizeof(val));
}

int StdDevice_GPIO_read(PikaObj *self){
    pika_dev* dev = _get_dev(self);
    // LOG("<%s> dev->is_enabled=%ld\n", __func__, dev->is_enabled);
    uint32_t val = 0;
    pika_hal_read(dev, &val, sizeof(val));
    return val;
}


void StdDevice_GPIO_enable_IT(PikaObj *self, int irq, Arg* evt_cbk){
#if PIKA_EVENT_ENABLE
    pika_dev* dev = _get_dev(self);

    _StdDevice_setCallback(self, evt_cbk, (uintptr_t)dev);
    /* regist event to pika_hal */
    pika_hal_GPIO_config cfg_cb = {0};
    cfg_cb.event_callback = _StdDevice_GPIO_event_handler;
    cfg_cb.event_callback_filter = irq;
    cfg_cb.event_callback_ena = PIKA_HAL_EVENT_CALLBACK_ENA_ENABLE;
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg_cb);
#else
    obj_setErrorCode(self, PIKA_RES_ERR_INVALID_PARAM);
    obj_setSysOut(self, "[error] PIKA_EVENT_ENABLE is disabled.");
#endif
}

void StdDevice_GPIO_disable_IT(PikaObj *self, int irq){
    pika_dev* dev = _get_dev(self);
    _StdDevice_CancelCallback(self, (uintptr_t)dev);
    /* regist event to pika_hal */
    pika_hal_GPIO_config cfg_cb = {0};
    cfg_cb.event_callback = NULL;
    cfg_cb.event_callback_filter = irq;
    cfg_cb.event_callback_ena = PIKA_HAL_EVENT_CALLBACK_ENA_DISABLE;
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg_cb);
}

void StdDevice_GPIO_set_mode(PikaObj *self, int mode){
    pika_hal_GPIO_config cfg = {0};
    cfg.dir = mode;
    pika_dev* dev = _get_dev(self);
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg);
}


void StdDevice_GPIO_set_pull(PikaObj *self, int pull){
    pika_hal_GPIO_config cfg = {0};
    cfg.pull = pull;
    pika_dev* dev = _get_dev(self);
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg);
}

void StdDevice_GPIO_set_speed(PikaObj *self, int speed){
    pika_hal_GPIO_config cfg = {0};
    cfg.speed = speed;
    pika_dev* dev = _get_dev(self);
    pika_hal_ioctl(dev, PIKA_HAL_IOCTL_CONFIG, &cfg);
}



int StdDevice_GPIO_get_pull(PikaObj *self){
    pika_dev* dev = _get_dev(self);
    pika_hal_GPIO_config* cfg_ptr = dev->ioctl_config;
    return cfg_ptr->pull;
}

int StdDevice_GPIO_get_mode(PikaObj *self){
    pika_dev* dev = _get_dev(self);
    pika_hal_GPIO_config* cfg_ptr = dev->ioctl_config;
    return cfg_ptr->dir;
}

int StdDevice_GPIO_get_speed(PikaObj *self){
    pika_dev* dev = _get_dev(self);
    pika_hal_GPIO_config* cfg_ptr = dev->ioctl_config;
    return cfg_ptr->speed;
}


